function getData(key) {
    let val = getItem(key)
    if (!isNormalVal(key)) {
        console.error('getData 非正常key', key)
        return null;
    }
    let obj;
    try {
        obj = JSON.parse(val);
    } catch (e) {
        obj = val;
    }
    return obj;
}

function setData(key, value) {
    if (isNormalVal(key)) {
        if (typeof value == 'object') {
            setItem(key, JSON.stringify(value))
        } else if (typeof value == 'number') {
            setItem(key, value)
        } else if (typeof value == 'boolean') {
            setItem(key, value)
        } else if (typeof value == 'string') {
            setItem(key, value)
        }
    } else {
        console.error('setData 非正常key', key)
    }
}

function removeAll() {
    cc.sys.localStorage.clear();
}

function isNormalVal(key) {
    if (key == undefined || key == null || key == '') {
        return false;
    }
    return true;
}

function setItem(key, value) {
    cc.sys.localStorage.setItem(key, value)
}
function getItem(key): any {
    return cc.sys.localStorage.getItem(key);
}

interface DrawOption {
    ctx?: cc.Graphics,
    radius?: number,
}
class GameHelper {
    static _instance: GameHelper = null;
    static getInstance(): GameHelper {
        if (!GameHelper._instance) {
            GameHelper._instance = new GameHelper();
        }
        return GameHelper._instance;
    }
    version: string = '1.0.0';

    loadResPromise(path, resType) {
        return new Promise<any>(success => {
            cc.loader.loadRes(path, resType, (e, res) => {
                if (e) {
                    console.error(e);
                    success(null);
                }
                success(res);
            })
        })
    }
    // 获取预制中的子节点
    prefabs = {};
    getSubPrefab(prefabName: string, childName: string): Promise<cc.Node> {
        return new Promise(success => {
            cc.loader.loadRes('prefab/' + prefabName, cc.Prefab, (e, prefab) => {
                if (e) {
                    console.error(e);
                    success(null);
                }
                // let name = `${prefabName}_PrefabInstance`;
                // if (!this[name]) {
                //     let instance = cc.instantiate(prefab);
                //     this[name] = instance;
                // }
                let instance: cc.Node;
                let ins;
                if (!this.prefabs[prefabName]) {
                    this.prefabs[prefabName] = cc.instantiate(prefab);
                }
                instance = this.prefabs[prefabName];
                let subNode = instance.getChildByName(childName);

                if (subNode) {
                    ins = cc.instantiate(subNode);
                } else {
                    console.error(childName);
                }
                success(ins);
            })
        })
    }
    /**获取主场景脚本 */
    getCore() {
        return cc.find('core') && cc.find('core')['_components'][0];
    }

    getNoRepeatItems<T>(len: number, list: T[]): { resList: T[], leftList: T[] } {
        if (len >= list.length) return;
        let nList = list.concat();
        let resList: T[] = []
        while (len > resList.length) {
            let idx = this.randomInteger(0, nList.length - 1);
            resList.push(nList.splice(idx, 1)[0])
        }
        return { resList: resList, leftList: nList }
    }

    /**v3兼容v2 */
    vec2(v: cc.Vec3): cc.Vec2 {
        return cc.v2(v.x, v.y);
    }
    /**v2兼容v3 */
    vec3(v: cc.Vec2): cc.Vec3 {
        return cc.v3(v.x, v.y, 0);
    }
    scale(number: number) {
        return cc.v3(number, number, number)
    }
    getContentedScale(inner: cc.Node, outter: cc.Node): cc.Vec3 {
        let srcScale = cc.v3()
        inner.getScale(srcScale);
        let dstScale = 1;
        if (inner && outter) {
            let innerT = inner;
            let outterT = outter;
            let oRect: cc.Rect
            let iRect: cc.Rect
            do {
                inner.setScale(dstScale)
                if (dstScale < 0) {
                    dstScale = 0;
                }
                oRect = gameHelper.getBoundingBoxToWorld(outter)
                iRect = gameHelper.getBoundingBoxToWorld(inner)
                iRect.x = oRect.x;
                iRect.y = oRect.y;
                dstScale -= 0.05
            } while (dstScale > 0 && oRect && iRect && !oRect.containsRect(iRect))
            inner.setScale(srcScale);
        }
        // console.log(dstScale)
        // console.log(inner, outter)
        return this.scale(dstScale);
    }
    /**primise版本的等待 */
    wait(second: number): Promise<any> {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(null);
            }, second * 1000);
        })
    }

    /**返回最近的包含的位置 */
    getPointLimitPos(pos: cc.Vec3, limitRect: cc.Rect): cc.Vec3 {
        if (pos.x < limitRect.x) {
            // console.log('x', limitRect.x - curRect.x)
            pos.x += (limitRect.x - pos.x);
        }
        if (pos.x > limitRect.x + limitRect.width) {
            // console.log('x2', (limitRect.x + limitRect.width) - (pos.x ))
            pos.x += ((limitRect.x + limitRect.width) - (pos.x));
        }
        if (pos.y < limitRect.y) {
            // console.log('y', limitRect.y - pos.y)
            pos.y += (limitRect.y - pos.y);
        }
        if (pos.y > limitRect.y + limitRect.height) {
            // console.log('y2', (limitRect.y + limitRect.height) - (pos.y ))
            pos.y += ((limitRect.y + limitRect.height) - (pos.y));
        }
        return pos;
    }

    /**返回最近的包含的位置 */
    getRectLimitPos(pos: cc.Vec2, curRect: cc.Rect, limitRect: cc.Rect): cc.Vec2 {
        if (curRect.x < limitRect.x) {
            // console.log('x', limitRect.x - curRect.x)
            pos.x += (limitRect.x - curRect.x);
        }
        if (curRect.x + curRect.width > limitRect.x + limitRect.width) {
            // console.log('x2', (limitRect.x + limitRect.width) - (curRect.x + curRect.width))
            pos.x += ((limitRect.x + limitRect.width) - (curRect.x + curRect.width));
        }
        if (curRect.y < limitRect.y) {
            // console.log('y', limitRect.y - curRect.y)
            pos.y += (limitRect.y - curRect.y);
        }
        if (curRect.y + curRect.height > limitRect.y + limitRect.height) {
            // console.log('y2', (limitRect.y + limitRect.height) - (curRect.y + curRect.height))
            pos.y += ((limitRect.y + limitRect.height) - (curRect.y + curRect.height));
        }
        return pos;
    }

    /**
     * @param curRect 
     */
    getRectFivePointVector(curRect: cc.Rect): { centre: cc.Vec2, leftTop: cc.Vec2, rightTop: cc.Vec2, rightBottom: cc.Vec2, leftBottom: cc.Vec2 } {
        return {
            centre: cc.v2(curRect.x + curRect.width / 2, curRect.y + curRect.height / 2),
            leftTop: cc.v2(curRect.x, curRect.y + curRect.height),
            rightTop: cc.v2(curRect.x + curRect.width, curRect.y + curRect.height),
            rightBottom: cc.v2(curRect.x + curRect.width, curRect.y),
            leftBottom: cc.v2(curRect.x, curRect.y),
        }
    }

    /**
     * 给定字符串和个数创建随机字符串数组
     * @param obj 
     */
    buildRdmStrArray(obj: { [key: string]: number }) {
        let arr: string[] = [];
        for (let i in obj) {
            let count = obj[i];
            for (let num = 0; num < count; num++) {
                arr.push(i.toString());
            }
        }
        return gameHelper.messArray(arr);
    }

    /** 混乱成随机数组 */
    messArray<T>(arr: T[]) {
        let backup = [].concat(arr);
        let newArr: T[] = [];
        let idx = 0;
        while (arr.length) {
            let rdm = arr.splice(Math.floor(Math.random() * arr.length), 1)[0]
            newArr[idx++] = rdm;
        }
        for (let i = 0; i < backup.length; i++) {
            arr[i] = backup[i];
        }
        return newArr;
    }



    /**分享按钮 */
    addShareBtn(x: number, y: number, parent?: cc.Node, callback?: Function) {
        if (!_PF.is_wx() && !_PF.is_tt() && !_PF.is_yywl() && !CC_PREVIEW) {
            return;
        }
        cc.loader.loadRes('images/ui', cc.SpriteAtlas, (e: Error, atlas: cc.SpriteAtlas) => {
            if (e) console.error(e)
            let res = atlas.getSpriteFrame('fenxiang');
            let curSize = cc.size(res.getOriginalSize().width * 0.65, res.getOriginalSize().height * 0.65);
            let afp = new cc.Node('afp')
            afp.position = cc.v2(x, y)
            afp.parent = parent || cc.find('Canvas');
            // console.log(res, curSize, afp)
            afp.setContentSize(curSize);
            afp.addComponent(cc.Button).transition = 3;
            afp.on(cc.Node.EventType.TOUCH_END, () => {
                gameHelper.share(() => { });
            })
            let afpImg = new cc.Node('afpImg');
            afpImg.addComponent(cc.Sprite).spriteFrame = res;
            afp.addChild(afpImg)
            afp.setContentSize(curSize);
            afpImg.setContentSize(curSize);
            cc.tween(afpImg)
                .to(1, { y: 10 })
                .to(1, { y: 0 })
                .to(1, { y: -10 })
                .union()
                .repeatForever()
                .start();
        })
    }

    /**递归的创建出整个树节点,如果存在就略过 */
    createNodeTree(treePath: string): cc.Node {
        let treePathArr = treePath.split('/').filter((v) => {
            return v;
        })
        let nodeName = treePathArr.splice(treePathArr.length - 1, 1).find(v => { return v });
        let parentTreePath = treePathArr.join('/');
        let parentNode: cc.Node;
        if (cc.find(parentTreePath)) {
            parentNode = cc.find(parentTreePath)
        } else {
            parentNode = this.createNodeTree(parentTreePath);
        }
        let node = parentNode.getChildByName(nodeName)
        if (!node) {
            node = new cc.Node(nodeName);
            node.parent = parentNode;
        }
        return node;
    }

    getDebugSceneCtx(): cc.Graphics {
        let debugCtxNode = cc.find('debugCtxNode');
        let ctx;
        if (!debugCtxNode) {
            debugCtxNode = new cc.Node('debugCtxNode');
            debugCtxNode.parent = cc.director.getScene();
            ctx = debugCtxNode.addComponent(cc.Graphics);
            ctx.lineWidth = 4;
            ctx.strokeColor = cc.color(255, 0, 0);
        }
        if (!debugCtxNode.getComponent(cc.Graphics)) {
            debugCtxNode.addComponent(cc.Graphics);
        }
        return debugCtxNode.getComponent(cc.Graphics);
    }

    getTargetCtx(target: cc.Node): cc.Graphics {
        let ctx;
        target = target || cc.director.getScene();
        let ctxNode = target.getChildByName('curTargetCtx');
        if (!ctxNode) {
            ctxNode = new cc.Node('curTargetCtx');
            ctxNode.parent = target;
            ctxNode.position = cc.v2();
            ctx = ctxNode.addComponent(cc.Graphics);
            ctx.lineWidth = 4;
            ctx.strokeColor = cc.color(255, 0, 0);
            ctxNode.zIndex = cc.macro.MAX_ZINDEX;
        }
        if (!ctxNode.getComponent(cc.Graphics)) {
            ctxNode.addComponent(cc.Graphics);
        }
        return ctxNode.getComponent(cc.Graphics);
    }
    // 显示debug信息
    showDebugInfoByLabel(str: string) {
        if (CC_DEBUG) {
            let strNode = cc.find('DebugInfoLabel');
            if (!strNode) {
                let strNode = new cc.Node('DebugInfoLabel');
                strNode.parent = cc.director.getScene();
                strNode.position = cc.v2(0, cc.winSize.height * 0.8);
                strNode.addComponent(cc.Label);
                strNode.zIndex = cc.macro.MAX_ZINDEX;
                strNode.group = 'ui';
                strNode.color = cc.color(255, 0, 0)
                let wdg = strNode.addComponent(cc.Widget);
                wdg.isAlignLeft = true;
                wdg.left = -50;
                wdg.alignMode = cc.Widget.AlignMode.ALWAYS;
            }
            strNode = cc.find('DebugInfoLabel');
            let label = strNode.getComponent(cc.Label);
            label.string = str;
        }
    }
    // 俩节点是否足够近
    isCloseEnough(node1: (cc.Node | cc.Vec2 | cc.Vec3), node2: (cc.Node | cc.Vec2 | cc.Vec3), distLimit: number) {
        let wpos1, wpos2;
        distLimit = distLimit >= 0 ? distLimit : Math.abs(distLimit);
        wpos1 = this.getWpos(node1);
        wpos2 = this.getWpos(node2);
        if (!wpos1 || !wpos2) {
            return false;
        }
        // console.log('wpos1.sub(wpos2).mag()',wpos1.sub(wpos2).mag())
        return wpos1.sub(wpos2).mag() <= distLimit;
    }
    /**
     * 是否node1相交node2
     * @param node1 
     * @param node2 
     * @returns 
     */
    isRectIntersect(node1: cc.Node, node2: cc.Node) {
        let rect1 = this.getBoundingBoxToWorld(node1);
        let rect2 = this.getBoundingBoxToWorld(node2);
        // gameHelper.drawAny(null, rect1, rect2)
        return rect1.intersects(rect2)
    }
    /**
     * node1是否包含node2
     * @param node1 
     * @param node2 
     * @returns 
     */
    isContainRect(node1: cc.Node, node2: cc.Node) {
        return gameHelper.getBoundingBoxToWorld(node1).containsRect(gameHelper.getBoundingBoxToWorld(node2))
    }

    // 切换父节点的时候保持原来的位置
    switchParentHoldPos(node: cc.Node, parent: cc.Node) {
        node.position = parent.convertToNodeSpaceAR(gameHelper.getWpos(node));
        if (node.getComponent('MoveEvent')) {
            node.getComponent('MoveEvent').destPos = node.position;
        }
        node.parent = parent;
    }
    // 获取两节点之间的距离
    getDistBetween(node1: cc.Node, node2: cc.Node) {
        let wpos1 = node1.parent.convertToWorldSpaceAR(node1.position);
        let wpos2 = node2.parent.convertToWorldSpaceAR(node2.position);
        return wpos1.sub(wpos2).mag();
    }

    getMostCloseFromList(node: (cc.Node | cc.Vec2 | cc.Vec3), list: cc.Node[], distLimit?: number, nodesOffset: cc.Vec2 = cc.Vec2.ZERO): cc.Node {
        let mostCloseNode: cc.Node = null;
        let mostCloseDist: number = null;
        let listNodeWpos: cc.Vec2, curDist: number, nodeWpos: cc.Vec2;
        nodeWpos = gameHelper.getWpos(node, true);
        // console.log('node', node)
        // let ctx = gameHelper.getDebugSceneCtx();
        // ctx.clear();
        // console.log('nodenode', node)
        if (nodeWpos && list.length) {
            for (let i = 0; i < list.length; i++) {
                let listNode = list[i];
                if (listNode && listNode.isValid) {
                    // listNodeWpos = listNode.parent.convertToWorldSpaceAR(listNode.position);
                    listNodeWpos = gameHelper.getWpos(listNode, true);
                    listNodeWpos.addSelf(nodesOffset)
                    // ctx.circle(listNodeWpos.x, listNodeWpos.y, 30);
                    // ctx.stroke();
                    // ctx.fill();
                    curDist = nodeWpos.sub(listNodeWpos).mag();
                    if (mostCloseDist == null || curDist < mostCloseDist) {
                        // console.log('curDist', curDist);
                        mostCloseNode = listNode;
                        mostCloseDist = curDist;
                    }
                }
            }
        }
        if (typeof distLimit == 'number') {
            // console.log('有限制距离', distLimit, mostCloseNode);
            if (gameHelper.isCloseEnough(node, mostCloseNode, distLimit)) {
                return mostCloseNode;
            }
            // console.log('超过限制距离');
            return null;
        }
        return mostCloseNode;
    }
    /**查找目标节点下的子节点 */
    find(target: cc.Node, path: string) {
        let child: cc.Node = target;
        if (target && path) {
            let pathSplitArr = path.split('/').filter((v) => {
                return v != '';
            });
            ;
            while (pathSplitArr.length && child && child.getChildByName(pathSplitArr[0])) {
                let name = pathSplitArr.shift();
                child = child.getChildByName(name);
                // console.log('name', child, pathSplitArr);
            }
            if (pathSplitArr.length > 0) {
                return null;
            }
        }
        return child;
    }
    loadScene(scene_name: string, callback?: Function) {
        let camera;
        if (scene_name) {
            camera = cc.find('Canvas/Main Camera').getComponent(cc.Camera);
            camera.backgroundColor = cc.color(255, 255, 255)
            cc.tween(cc.find('Canvas'))
                .to(0.5, { opacity: 0 })
                .call(() => {
                    cc.director.loadScene(scene_name, () => {
                        cc.find('Canvas').opacity = 0;
                        camera = cc.find('Canvas/Main Camera').getComponent(cc.Camera);
                        camera.backgroundColor = cc.color(255, 255, 255)
                        let lastColor = camera.backgroundColor;
                        cc.tween(cc.find('Canvas'))
                            .to(1, { opacity: 255 })
                            .call(() => {
                                camera.backgroundColor = lastColor;
                            })
                            .start()
                        if (callback) callback();
                    })
                })
                .start()
        }
    }

    /**判断是否是第一次玩 */
    isFirstPlay() {
        if (window.IS_FIRST_PLAY == undefined) {
            if (!getData('isPlayed')) {
                window.IS_FIRST_PLAY = true;
                console.warn('第一次玩')
                removeAll();
                setData('isPlayed', true);
            } else {
                console.log('非第一次玩')
                window.IS_FIRST_PLAY = false;
            }
        }
        return window.IS_FIRST_PLAY;
    }
    /**每次分配的id都会+1，并且保存一个数组，可以回收 */
    distributeItemId(): number {
        let lastItemId = gameHelper.getLastItemId();
        let curItemId = 1;
        let recycleItemIdList = gameHelper.getRecycleItemIdList();
        // 如果有被回收的ItemId就从id池里抽出第一个，否则就+1
        if (recycleItemIdList.length) {
            // console.log('distributeItemId1', recycleItemIdList)
            curItemId = recycleItemIdList.shift();
            gameHelper.setRecycleItemIdList(recycleItemIdList);
        } else {
            // console.log('distributeItemId2', lastItemId)
            curItemId = lastItemId + 1;
            gameHelper.setLastItemId(curItemId);
        }

        return Number(curItemId);
    }

    getItemInfoKey(itemId: string) {
        return `itemInfo_${itemId}`;
    }
    setCustomConfig(id: (string | number), key: string, val: any) {
        if (id == null || id == undefined || id == '') {
            console.error('id为空')
            return;
        }
        if (typeof id == "string" && id.length != 22) {
            // console.error('非法的uuid长度', id, id.length)
        }
        if (typeof id == "number") {
            id = id.toString();
        }
        let data = gameHelper.loadRecoderInfoByItemId(id) || {};
        data[key] = val;
        gameHelper.saveRecoderInfoByItemId(id, data);
    }
    /**
     * 获取节点自定义配置的数据，可赋默认值
     * @param id 
     * @param key 字段 
     * @param out 输出
     * @param defaultVal 默认值
     * @returns 
     */
    getCustomConfig(id: (number | string), key: string, out?: any, defaultVal?: any): any {
        if (id == null || id == undefined || id == '') {
            console.error('id为空')
            return;
        }
        if (typeof id == "string" && id.length != 22) {
            // console.error('非法的uuid长度', id, id.length)
        }
        if (typeof id == "number") {
            id = id.toString();
        }
        let data = gameHelper.loadRecoderInfoByItemId(id);
        let val;
        if (data && data[key] != undefined) {
            val = data[key];
        }

        if (out instanceof Object) {
            if ((val == undefined || val == null) && defaultVal != null) {
                val = defaultVal;
                // console.log('getCustomConfig defaultVal', out, val)
            }
            out[key] = val;
            // console.log('getCustomConfig val', key, val)
        }
        return val;
    }
    saveRecoderInfoByItemId(id: string, data: any) {
        let itemIdkey = gameHelper.getItemInfoKey(id);
        setData(itemIdkey, data)
    }
    loadRecoderInfoByItemId(id: string): any {
        let itemIdkey = gameHelper.getItemInfoKey(id);
        return getData(itemIdkey);
    }
    /**回收ItemId */
    recycleItemId(itemId: number) {
        let recycleItemIdList = gameHelper.getRecycleItemIdList();
        recycleItemIdList.push(itemId);
        gameHelper.setRecycleItemIdList(recycleItemIdList);
    }
    /**获取id回收站列表 */
    getRecycleItemIdList(): Array<number> {
        return getData('recycleItemIdList') || []
    }
    /**保存id回收站列表 */
    setRecycleItemIdList(v: Array<number>) {
        setData('recycleItemIdList', v)
    }
    /**获取最新id */
    getLastItemId() {
        if (getData('lastItemId') == undefined) {
            setData('lastItemId', -1)
        }
        return getData('lastItemId');
    }
    /**保存最新id */
    setLastItemId(v: number) {
        setData('lastItemId', v);
    }
    /**导出某个组件所有节点数据 */
    exportItemFormatByComps(name: string) {
        let nodes = gameHelper.getNodesByComponents(name)
        if (nodes.length) {
            gameHelper.getExportData(nodes)
        }
    }
    exportItemFormatRecursive(node: cc.Node) {
        let nodes = gameHelper.getNodesByComponents('ThingBase', node);
        if (nodes.length) {
            if (!nodes.includes(node)) {
                nodes = nodes.concat([node]);
            }
            gameHelper.getExportData(nodes)
        }
    }

    /**格式化导出item */
    getExportData(nodes: (cc.Node | cc.Node[])) {
        if (nodes instanceof cc.Node) {
            nodes = [nodes];
        }
        if (CC_PREVIEW) {
            let data = '';
            for (let i = 0; i < nodes.length; i++) {
                let node = nodes[i];
                data +=
                    `
{
    itemId: null,
    scene: sceneList.${cc.director.getScene().name},
    name: '${node.name}',
    position: cc.v2(${node.x},${node.y}),
    path: '${gameHelper.getNodeTreePath(node)}',
    angle: ${node.angle},
},`
            }
            console.log(data);
            return data;
        }
    }
    /**
     * 
     * @param args 任意节点
     * @returns 是否都存在
     */
    isExistNode(...args: cc.Node[]) {
        let res = true;
        for (let i of args) {
            if (!i || !i.isValid || !(i instanceof cc.Node)) {
                res = false;
                if (CC_PREVIEW) {
                    console.error('有不存在的节点，请检查：', args);
                }
            }
        }
        // console.log('isExistNode', res)
        return res;
    }
    /**
     * 
     * @param node 需要改位置的node
     * @param target 参照物node
     */
    setPositionToAlignNode(node: cc.Node, target: cc.Node, offset: cc.Vec2 = cc.Vec2.ZERO) {
        if (node && node.isValid && target && target.isValid) {
            let wpos = target.parent.convertToWorldSpaceAR(target.position);
            node.position = node.parent.convertToNodeSpaceAR(wpos).add(offset);
        }
    }
    /**
     * 
     * @param node 需要改位置的node
     * @param target 参照物node
     */
    getPositionToAlignNode(node: cc.Node, target: cc.Node, offset: cc.Vec2 = cc.Vec2.ZERO) {
        if (node && node.isValid && target && target.isValid) {
            let wpos = target.parent.convertToWorldSpaceAR(target.position);
            return node.parent.convertToNodeSpaceAR(wpos).add(offset);
        }

    }
    randomItem(list: any[]) {
        let rdmIdx = gameHelper.randomInteger(0, list.length - 1)
        return list[rdmIdx];
    }
    /**
     * 获取节点世界坐标
     * @param node 目标节点
     * @param isCentre 是否强制获取节点中心点
     * @returns 
     */
    getWpos(node: (cc.Node | cc.Vec2 | cc.Vec3), isCentre: boolean = false): cc.Vec2 {
        let wpos;
        if (node instanceof cc.Node && node && node.isValid) {
            wpos = node.convertToWorldSpaceAR(cc.Vec2.ZERO);
            if (isCentre) {
                //计算中心点的位置
                let center = cc.v2();
                center.x = (0.5 - node.anchorX) * node.width;
                center.y = (0.5 - node.anchorY) * node.height;
                // console.log('center', center)
                wpos = node.convertToWorldSpaceAR(center);
            }
        }
        if (node instanceof cc.Vec2 || node instanceof cc.Vec3) {
            wpos = cc.v2(node);
        }
        return wpos;
    }
    setWpos(node: cc.Node, Wpos: cc.Vec2) {
        node.setPosition(node.parent.convertToNodeSpaceAR(Wpos));
    }
    getBoundingBoxToWorld(node: cc.Node): cc.Rect {
        if (!node) {
            console.error('没有节点')
            return;
        }
        let rect = node.getBoundingBox();
        let wpos = node.parent.convertToWorldSpaceAR(cc.v2(rect.x, rect.y));
        let wRect = new cc.Rect(
            wpos.x,
            wpos.y,
            rect.width,
            rect.height,
        )
        return wRect;
    }
    /**获取节点包围盒的四个顶点 */
    getQuadPoints(node: cc.Node): cc.Vec2[] {
        let points: cc.Vec2[] = [];
        let wRect = gameHelper.getBoundingBoxToWorld(node);
        points = [
            cc.v2(wRect.x, wRect.y),
            cc.v2(wRect.x, wRect.y + wRect.height),
            cc.v2(wRect.x + wRect.width, wRect.y + wRect.height),
            cc.v2(wRect.x + wRect.width, wRect.y),
        ]

        return points;
    }
    rdmColor(): cc.Color {
        return cc.color(
            gameHelper.randomInteger(0, 255),
            gameHelper.randomInteger(0, 255),
            gameHelper.randomInteger(0, 255),
        )
    }
    /**清理画笔，默认debug画笔 */
    clearDraw(ctx?: cc.Graphics) {
        ctx = ctx || gameHelper.getDebugSceneCtx();
        ctx.clear();
    }
    drawCircle(radius: number, wpos: cc.Vec2, ctx?: cc.Graphics) {
        ctx = ctx || gameHelper.getDebugSceneCtx();
        let curRadius = radius || 10;
        let pos = ctx.node.convertToNodeSpaceAR(wpos);
        ctx.circle(pos.x, pos.y, curRadius);
        ctx.stroke();
    }
    drawRect(rect: cc.Rect, ctx?: cc.Graphics) {
        let pos = ctx.node.convertToNodeSpaceAR(cc.v2(rect.x, rect.y));
        ctx.rect(pos.x, pos.y, rect.width, rect.height);
        ctx.stroke();
    }
    drawLine(points: cc.Vec2[], ctx?: cc.Graphics) {
        ctx = ctx || gameHelper.getDebugSceneCtx();
        let pos = ctx.node.convertToNodeSpaceAR(points[0]);
        ctx.moveTo(pos.x, pos.y);
        for (let i = 0; i < points.length; i++) {
            let p = points[i]
            let pos = ctx.node.convertToNodeSpaceAR(p);
            ctx.lineTo(pos.x, pos.y);
            ctx.moveTo(pos.x, pos.y);
        }
        ctx.stroke();
        ctx.fill();
    }

    drawAny(drawOption?: DrawOption, ...anyList: (cc.Rect | cc.Vec2 | cc.Vec2[])[]) {
        let curRadius = 10;
        let ctx = gameHelper.getDebugSceneCtx();
        if (drawOption) {
            curRadius = drawOption.radius;
            ctx = drawOption.ctx;
        }
        for (let any of anyList) {
            if (any instanceof cc.Vec2) {
                gameHelper.drawCircle(curRadius, any, ctx)
            } else if (any instanceof cc.Rect) {
                gameHelper.drawRect(any, ctx)
            } else if (any instanceof Array) {
                gameHelper.drawLine(any, ctx);
            }
        }
    }


    /**
     * 
     * @param target tween的目标
     * @param isClearTween 是否保存tween以便下次清理
     * @returns 
     */
    tween(target?: Object, isClearTween: boolean = false, tweenKey: string = 'currentTween'): cc.Tween {
        if (!target) {
            console.error('no target');
            return
        }
        if (target[tweenKey] && target[tweenKey] instanceof cc.Tween) {
            target[tweenKey].stop();
            target[tweenKey] = null;
        }
        let curTween = cc.tween(target);

        if (isClearTween) {
            target[tweenKey] = curTween;
        }
        return curTween;
    }
    /**随机number */
    randomNum(min, max): number {
        let num = min + Math.random() * (max - min);
        return num;
    }
    /**
     * 
     * @param min 最小值
     * @param max 最大值
     * @returns 随机整数(包含最大值和最小值)
     */
    randomInteger(min, max): number {
        max += 1;
        let num = min + Math.random() * (max - min);
        return Math.floor(num);
    }

    tweenPromise(tween: cc.Tween) {
        return new Promise(success => {
            tween
                .call(() => {
                    success(true);
                })
                .start();
        })
    }
    tweenPromiseAll(tweens: cc.Tween[]) {
        return new Promise(success => {
            let promiseArr = [];
            tweens.forEach(v => {
                promiseArr.push(gameHelper.tweenPromise(v))
            })
            Promise.all(promiseArr).then((arr) => {
                success(true);
            })
        })
    }
    tweenPromiseRace(tweens: cc.Tween[]) {
        return new Promise(success => {
            let promiseArr = [];
            tweens.forEach(v => {
                promiseArr.push(gameHelper.tweenPromise(v))
            })
            Promise.race(promiseArr).then(() => {
                success(true);
            })
        })
    }
    getRealTouchWpos(touch: cc.Event.EventTouch): cc.Vec2 {
        let camera = cc.find('Canvas/Main Camera');
        return touch.getLocation().add(cc.v2(camera.x, 0));
    }

    firstLetterToUpperCase(str: string) {
        return str.substr(0, 1).toLocaleUpperCase() + str.substr(1);
    }
    setSwallowTouches(node: cc.Node, is: boolean) {
        if (node && node['_touchListener'] && typeof node['_touchListener'].setSwallowTouches == 'function') {
            node['_touchListener'].setSwallowTouches(is);
        } else {
            console.error('没有节点')
        }
    }
    toColor(node: cc.Node, color: cc.Color, time = 1) {
        return new Promise(success => {
            gameHelper.tween(node, true)
                .to(time, { color: color, scaleY: 1 })
                .call(() => {
                    success(null)
                })
                .start();
        })
    }
    toColorRecursive(node: cc.Node, color: cc.Color, time = 1) {
        return new Promise(success => {
            let nodes = gameHelper.getChildrenRecursive(node);
            let promiseArr = [];
            for (let child of nodes) {
                promiseArr.push(
                    gameHelper.tween(child, true)
                        .to(time, { color: color, scaleY: 1 })
                        .start()
                )
            }
            Promise.all(promiseArr).then(() => {
                success(null)
            })
        })
    }
    addCompOnce<T extends cc.Component>(node: cc.Node, compName: { new(): T }): T
    addCompOnce(node: cc.Node, compName: string): any
    addCompOnce<T extends cc.Component>(node: cc.Node, compName: (string | { new(): T })): (T | any) {
        if (typeof compName == 'string' && cc.js.getClassByName(compName)) {
            if (node && !node.getComponent(compName)) {
                node.addComponent(compName);
            }
            return node.getComponent(compName);
        } else if (typeof compName != 'string') {
            if (node && !node.getComponent(compName)) {
                node.addComponent(compName);
            }
            return node.getComponent(compName);
        }
    }
    /**事件全部转发给另一个节点 */
    transferTouchEvent(from: cc.Node, to: cc.Node) {
        from.on(cc.Node.EventType.TOUCH_START, (touch: cc.Event.EventTouch) => {
            touch.stopPropagation()
            to.emit(cc.Node.EventType.TOUCH_START, touch)
        }, this);
        from.on(cc.Node.EventType.TOUCH_MOVE, (touch: cc.Event.EventTouch) => {
            touch.stopPropagation()
            to.emit(cc.Node.EventType.TOUCH_MOVE, touch)
        }, this);
        from.on(cc.Node.EventType.TOUCH_END, (touch: cc.Event.EventTouch) => {
            touch.stopPropagation()
            to.emit(cc.Node.EventType.TOUCH_END, touch)
        }, this);
        from.on(cc.Node.EventType.TOUCH_CANCEL, (touch: cc.Event.EventTouch) => {
            touch.stopPropagation()
            to.emit(cc.Node.EventType.TOUCH_CANCEL, touch)
        }, this);
    }
    updateZIndexByYaxis(node) {
        let zIndex = 10000 - node.y;
        if (typeof zIndex == 'number') {
            if (zIndex > 20000) {
                return;
            }
            node.zIndex = zIndex;
            // this.node.setSiblingIndex(this.node.x);
        }
    }
    getSceneList(): string[] {
        let sceneInfo = cc.game['_sceneInfos'];
        let sceneList = [];
        sceneInfo.forEach(v => {
            let res = v.url.match(/\w+\.fire/);
            if (res && res[0] != 'test.fire') {
                let str = res[0];
                sceneList.push(str.substr(0, str.length - 5));
            }
        });
        return sceneList;
    }
    addColorBg(color: (string | cc.Color)) {
        if (!cc.find('Canvas/colorBg')) {
            let colorBg = new cc.Node('colorBg')
            colorBg.parent = cc.find('Canvas');
            colorBg.addComponent(cc.Sprite)
            colorBg.position = cc.v2();

            let texture = new cc.Texture2D();
            // @ts-ignore
            texture.initWithData(new Uint8Array([255, 255, 255, 255]), cc.Texture2D.PixelFormat.RGBA8888, 1, 1);
            colorBg.getComponent(cc.Sprite).spriteFrame = new cc.SpriteFrame(texture);
            colorBg.setContentSize(cc.winSize.width * 5, cc.winSize.height * 5);
            colorBg.zIndex = -cc.macro.MAX_ZINDEX;
        }
        let colorBg = cc.find('Canvas/colorBg');
        if (typeof color == 'string') {
            color = cc.color().fromHEX(color);
        }
        colorBg.color = color;
    }


    lookAt(node: cc.Node, target: cc.Node) {
        let nodeWpos = gameHelper.getWpos(node)
        let targetWpos = gameHelper.getWpos(target)
        let dir = nodeWpos.sub(targetWpos)
        let radian = Math.atan2(dir.x, dir.y);
        let angle = -radian / Math.PI * 180 + 180;
        node.angle = angle;
    }
    getRelativeAngle(node: cc.Node, target: cc.Node) {
        let nodeWpos = gameHelper.getWpos(node)
        let targetWpos = gameHelper.getWpos(target)
        let dir = nodeWpos.sub(targetWpos)
        let radian = Math.atan2(dir.x, dir.y);
        let angle = -radian / Math.PI * 180 + 180;
        return angle;
    }



    addWxCustomAd(pos1 = cc.v2(0, 0.77), pos2 = cc.v2(0.92, 0.77)): void {
        if (_PF.is_wx() && !_PF.is_tt() && _G.banner_panduan) {
            // if (_PF.customads.length >= 2) {
            //     for (let ad of _PF.customads) {
            //         if (!ad.isShow()) {
            //             ad.show();
            //         }
            //     }
            //     console.log('add_custom 1');
            //     return;
            // }
            console.log('addWxCustomAd');

            _PF.destroy_customad();

            _PF.create_customad(cc.view.getFrameSize().width * pos1.x, cc.view.getFrameSize().height * pos1.y);
            _PF.create_customad(cc.view.getFrameSize().width * pos2.x, cc.view.getFrameSize().height * pos2.y);

        }
    }

    createInterad() {
        console.log('createInterad创建插屏！！')
        if (_PF.is_vivo()) {
            _G.isShowVivoNativeAd = !_G.isShowVivoNativeAd;
            if (_G.isShowVivoNativeAd) {
                _PF.create_nativead();
            } else {
                _PF.create_interad && _PF.create_interad();
            }
        } else {
            _PF.create_interad && _PF.create_interad();
        }
    }
    /**
     * 把具体的秒数转成时间格式的字符串
     * @param sec 秒
     * @param format h表示小时、m表示分钟、s表示秒  eg. "h:m:s" 或者 "h - m - s" 
     */
    secondToTimeFormat(sec: number, format: string) {
        let h = ~~(sec / 3600)
        sec -= h * 3600;
        let m = ~~(sec / 60)
        sec -= m * 60;
        let s = ~~(sec);
        format = format.replace(/h+/g, function (word) {
            let res = h.toString();
            while (word.length > res.length) {
                res = '0' + res;
            }
            return res;
        });
        format = format.replace(/m+/g, function (word) {
            let res = m.toString();
            while (word.length > res.length) {
                res = '0' + res;
            }
            return res;
        });
        format = format.replace(/s+/g, function (word) {
            let res = s.toString();
            while (word.length > res.length) {
                res = '0' + res;
            }
            return res;
        });
        // console.log('val', format, h, m, s)
        return format;
    }


    /**
     * 通过组件获取节点，找到第一个就返回
     * @param compName 组件名
     * @param target 父节点，默认场景根节点
     * @param distLimit 距离范围 
     * @returns 
     */
    getNodeByComponent(compName: string, target?: cc.Node): cc.Node {
        target = target || cc.director.getScene();
        let comp = target.getComponentInChildren(compName);
        return comp && comp.node;
    }

    /**
     * 通过组件获取节点数组
     * @param compName 组件名
     * @param target 父节点，默认场景根节点
     * @param distLimit 距离范围 
     * @returns 
     */
    getNodesByComponents(compName: string, target?: cc.Node): cc.Node[] {
        let nodes = [];
        target = target || cc.director.getScene();
        let comps = target.getComponentsInChildren(compName);
        if (comps.length) {
            let comp;
            for (let i = 0; i < comps.length; i++) {
                comp = comps[i];
                if (comp && comp.node && comp.node.isValid) {
                    nodes.push(comp.node);
                }
            }
        }
        return nodes;
    }

    /**通过组件获取最近的节点 */
    getMostCloseNodeByComponents(node: (cc.Node | cc.Vec2 | cc.Vec3), compName: string, distLimit?: number, nodesOffset: cc.Vec2 = cc.Vec2.ZERO): cc.Node {
        let nodes = gameHelper.getNodesByComponents(compName)
        let res: cc.Node;
        // console.log('nodes', nodes)
        nodes = nodes.filter((v: cc.Node) => {
            return v != node;
        })
        if (nodes.length) {
            res = gameHelper.getMostCloseFromList(node, nodes, distLimit, nodesOffset);
            // console.log('res', res)
        }
        return res;
    }

    /**获取节点树路径（不包括自身节点名） */
    getNodeTreePath(node: cc.Node, targetParent?: cc.Node): string {
        let path = '';
        let scene: cc.Node = cc.director.getScene();
        if (targetParent) {
            scene = targetParent;
        }
        if (node && node.isValid) {
            let target = node.parent;
            while (target && target.parent && target != scene) {
                path = '/' + target.name + path;
                target = target.parent;
            }
        }
        return path.substr(1);
    }

    /**通过节点名字深度查找获取第一个相同名字的子节点 */
    getChildByNameDeeply(name: string, target: cc.Node = cc.director.getScene()): cc.Node {
        let list = gameHelper.getChildrenRecursive(target);
        let node = list.find((v) => {
            return v.name == name;
        })
        return node;
    }
    /**通过节点名字深度查找获取所有相同名字的子节点 */
    getChildrenByNameDeeply(name: string, target: cc.Node = cc.director.getScene()): cc.Node[] {
        let list = gameHelper.getChildrenRecursive(target);
        list = list.filter((v) => {
            return v.name == name;
        })
        return list;
    }
    /**递归的获取所有子节点 */
    getChildrenRecursive(node: cc.Node) {
        if (!node || !node.isValid) {
            return;
        }
        let children = node.children;
        if (node.childrenCount > 0) {
            for (let i of node.children) {
                children = children.concat(gameHelper.getChildrenRecursive(i));
            }
        }
        return children;
    }
    /**递归的获取所有父节点 */
    getParentsRecursive(node: cc.Node): cc.Node[] {
        if (!node || !node.isValid) {
            return [];
        }
        let parents = [node.parent];
        return parents.concat(gameHelper.getParentsRecursive(node.parent));
    }
    /**返回子节点中第一个匹配组件 */
    getComponentInChildren<T extends cc.Component>(compName: string, node: cc.Node = cc.director.getScene()): T {
        if (typeof compName == 'string') {
            return <T>node.getComponentInChildren(compName);
        }
        return <T>node.getComponentInChildren(compName);
    }
    /**返回子节点中所有匹配到组件 */
    getComponentsInChildren<T extends cc.Component>(compName: string, node: cc.Node = cc.director.getScene()): T[] {
        if (typeof compName == 'string') {
            return <T[]>node.getComponentsInChildren(compName);
        }
        return <T[]>node.getComponentsInChildren(compName);
    }
    /**返回父节点中第一个匹配到组件 */
    getComponentInParents<T extends cc.Component>(compName: (string | { prototype: T }), node: cc.Node = cc.director.getScene()): T {
        let parents = gameHelper.getParentsRecursive(node)
        let res = parents.find((v) => {
            if (typeof compName == 'string') {
                return v && v.getComponent(compName);
            }
            return v && v.getComponent(compName);
        })
        // console.log('res', res);
        if (typeof compName == 'string') {
            return res && <T>res.getComponent(compName);
        }
        return res && <T>res.getComponent(compName);
    }
    /**返回子节点中所有匹配到组件 */
    getComponentsInParents<T extends cc.Component>(compName: (string | { prototype: T }), node: cc.Node = cc.director.getScene()): T[] {
        let comps: T[] = [];
        let parents = gameHelper.getParentsRecursive(node)
        parents.forEach((v) => {
            let comp;
            if (typeof compName == 'string') {
                comp = <T>v.getComponent(compName)
            } else {
                comp = <T>v.getComponent(compName)
            }
            if (comp) {
                comps.push(comp);
            }
        })
        return comps;
    }

    colorLerp(colorSrc: cc.Color, colorDst: cc.Color, ratio: number): cc.Color {
        if (colorSrc && colorDst && typeof ratio == 'number') {
            let sR = colorSrc.getR();
            let sG = colorSrc.getG();
            let sB = colorSrc.getB();
            let sA = colorSrc.getA();
            let dR = colorDst.getR();
            let dG = colorDst.getG();
            let dB = colorDst.getB();
            let dA = colorDst.getA();
            let resColor = cc.color(
                sR + (dR - sR) * ratio,
                sG + (dG - sG) * ratio,
                sB + (dB - sB) * ratio,
                sA + (dA - sA) * ratio,
            )
            return resColor;
        }
    }
    printRGBA(color: cc.Color) {
        return `${color.getR()}, ${color.getG()}, ${color.getB()}, ${color.getA()}`;
    }
    particleEffect(name: string, wpos: cc.Vec2, parent: cc.Node = cc.find('Canvas'), option?: Object) {
        gameHelper.getSubPrefab('particles', name).then((particle) => {
            particle.parent = parent;
            particle.position = parent.convertToNodeSpaceAR(wpos);
            particle.zIndex = cc.macro.MAX_ZINDEX;
            let particleSystem = particle.getComponent(cc.ParticleSystem);
            for (let i in option) {
                if (particleSystem[i] != undefined) {
                    particleSystem[i] = option[i];
                }
            }

        })
    }

    toast(msg: string) {
        console.log(msg);
    }


    //创建收藏按钮
    create_shoucang_btn(x?: number, y?: number, parent?: cc.Node, callback?: Function) {
        if (_PF.is_wx() && !_PF.is_tt()) {
            if (_G.shoucang_nodes && _G.shoucang_nodes.length > 0) {
                for (let i of _G.shoucang_nodes) {
                    i.isValid && i.destroy();
                }
            }
            x = x || cc.winSize.width / 2 * 0.52;
            y = y || cc.winSize.height / 2 * 0.8;
            _G.shoucang_nodes = [];
            cc.loader.loadRes("images/shoucang", cc.SpriteFrame, (err, res) => {
                if (err) {
                    console.log(err)
                    return
                }
                let shoucang_node = new cc.Node("shoucang_node")
                _G.shoucang_nodes.push(shoucang_node);
                parent = parent || cc.find("Canvas/ui") || cc.find("Canvas");
                shoucang_node.parent = parent
                shoucang_node.zIndex = cc.macro.MAX_ZINDEX
                shoucang_node.position = cc.v2(x, y)
                shoucang_node.addComponent(cc.Button)
                shoucang_node.scale = 0.5;
                let wd = shoucang_node.addComponent(cc.Widget)
                wd.isAlignRight = true;
                wd.isAlignTop = true;
                wd.top = 30;
                wd.right = 200;
                wd.alignMode = cc.Widget.AlignMode.ALWAYS;
                wd.updateAlignment();

                let btn = shoucang_node.getComponent(cc.Button)
                btn.transition = 3
                shoucang_node.on(cc.Node.EventType.TOUCH_END, () => {
                    shoucang_node.destroy();
                });
                shoucang_node.setContentSize(res._originalSize);
                let shoucang_sprite = new cc.Node("shoucang_sprite")
                shoucang_sprite.parent = shoucang_node
                shoucang_sprite.addComponent(cc.Sprite)
                shoucang_sprite.getComponent(cc.Sprite).spriteFrame = res;
                shoucang_sprite.runAction(cc.repeatForever(
                    cc.sequence(
                        cc.moveBy(2, 10, 0).easing(cc.easeInOut(2)),
                        cc.moveBy(2, -10, 0).easing(cc.easeInOut(2)),
                    )
                ))
                if (callback) callback(shoucang_node)
            })
        }
    }

    get_gameportad_btn_node() {
        return cc.find(_G.persist_node_tag + '/' + 'gameportad_btn');
    }
    del_gameportad_btn() {
        let node = gameHelper.get_gameportad_btn_node();
        if (node && node.isValid) {
            node.destroy();
        }
    }
    add_gameportad_btn(x: number, y: number) {
        if (!_PF.is_oppo() && !CC_PREVIEW) return;
        if (!_G.on_line) return;
        let gameportad_btn = gameHelper.get_gameportad_btn_node();
        if (gameportad_btn && gameportad_btn.isValid) {
            gameportad_btn.position = cc.v2(x, y);
            return;
        }
        cc.loader.loadRes('tuiguang/recommend/tuiguang_recommend', cc.SpriteAtlas, (err, atlas) => {
            if (err) {
                console.log('load tuiguang_recommend err', err)
                return;
            }
            let node = new cc.Node('gameportad_btn');
            node.addComponent(cc.Sprite).spriteFrame = atlas.getSpriteFrame('dialog_btn');

            node.position = cc.v2(x, y);
            node.parent = _G.get_persist_node();
            node.zIndex = 10;
            let pic = new cc.Node('pic');
            pic.addComponent(cc.Sprite).spriteFrame = atlas.getSpriteFrame('icon_' + Math.ceil(Math.random() * 4));
            pic.parent = node;
            pic.scale = 0;
            pic.position = cc.v2(0, 10)
            pic.runAction(cc.sequence(
                cc.scaleTo(0.25, 0.8),
                cc.callFunc(() => {
                    pic.runAction(cc.repeatForever(cc.sequence(
                        cc.rotateBy(0.1, 5),
                        cc.rotateBy(0.1, -5),
                        cc.rotateBy(0.1, -5),
                        cc.rotateBy(0.1, 5),

                        cc.rotateBy(0.1, 5),
                        cc.rotateBy(0.1, -5),
                        cc.rotateBy(0.1, -5),
                        cc.rotateBy(0.1, 5),
                        cc.delayTime(2),
                    )))
                })
            ))
            node.addComponent(cc.Button).transition = 3;
            node.on(cc.Node.EventType.TOUCH_END, () => {
                _PF.create_gameportalad && _PF.create_gameportalad();
            }, this);
        })
    }


    //原生插屏形式 示例_G.create_nativead_node()

    create_nativead_node(callback, exitBtnCallback) {
        if (!_G.on_line || (!_PF.is_oppo() && !_PF.is_vivo())) return;
        if (_PF.vivo_bannerAd) {
            _PF.vivo_bannerAd.hide();
        }
        _PF.create_nativead((nativead_node) => {
        }, () => {
            if (_PF.vivo_bannerAd) {
                _PF.vivo_bannerAd.show();
            }
        })
    }


    //原生Icon形式 示例 _G.create_nativeIcon_node(cc.winSize.width / 2 - 300, cc.winSize.height / 2 - 80);
    create_nativeIcon_node(x = 0, y = 0, callback?: Function) {
        if ((!_PF.is_oppo() && !_PF.is_vivo())) return;
        let node = _PF.get_nativead_node();
        if (node && node.isValid) {
            node.position = cc.v2(x, y);
        } else {
            _PF.create_nativead_tuiguang(x, y, 120);
        }
    }
    share(callback: Function) {

        if (CC_PREVIEW) {
            _G.toast('分享成功');
            if (callback) callback(true)
        }
        if (_PF.is_tt()) {
            _PF.recorderShare(function (res) {
                if (callback) callback(res)
            });
        } else if (_PF.is_wx()) {
            _PF.share();
            if (callback) callback(true)
        } else if (_PF.is_yywl()) {
            _PF.share(function (res) {
                if (callback) callback(res)
            });
        }
    }
    /**视频广告 */
    videoAd(callback: Function, videoid?: number) {

        let reward = (res: boolean) => {
            if (callback) callback(res)
            AudioMgr.resumeAll();
        }

        if (_G.toast) {
            _G.toast('正在拉取广告请稍等');
        }
        AudioMgr.pauseAll();
        if (CC_PREVIEW) {
            setTimeout(() => {
                if (reward) reward(Math.random() > 0.0 ? true : false)
            }, 1000)
        }

        if (_PF.is_tt()) {
            _PF.create_videoad(_GAMEINIT.tt_videoad_id, (res: boolean) => {
                reward(res)
            })
        } else if (_PF.is_wx()) {
            _PF.create_videoad(_GAMEINIT.wx_videoad_id, (res: boolean) => {
                reward(res)
            })
        } else if (_PF.is_vivo()) {
            _PF.create_videoad(_GAMEINIT.vivo_videoad_id, (res: boolean) => {
                reward(res)
            })
        } else if (_PF.is_oppo()) {
            _PF.create_videoad(_GAMEINIT.oppo_videoad_id, (res: boolean) => {
                reward(res)
            })
        } else if (_PF.is_android()) {
            _PF.create_videoad(null, (res: boolean) => {
                reward(res)
            })
        } else if (_PF.is_yywl()) {
            _PF.create_videoad(null, (res: boolean) => {
                reward(res)
            })
        } else if (_PF.is_facebook()) {
            console.log('facebook 视频广告')
            _PF.create_videoad(null, (res: boolean) => {
                reward(res)
            })
        }
    }
    createBannerad(type: string, isTop: boolean = false): void {
        if (type == PlatformType.wx) {
            if (_PF.is_wx() && !_PF.is_tt() && _G.banner_panduan) {
                gameHelper.destroyBannerad(type)
                _PF.create_bannerad(_GAMEINIT.wx_bannerad_id, isTop);
            }
        } else if (type == PlatformType.tt) {
            if (_PF.is_tt() && _G.on_line) {
                _PF.create_bannerad(_GAMEINIT.tt_bannerad_id, isTop);
            }
        } else if (type == PlatformType.yywl) {
            if (_PF.is_yywl() && !_PF.getIsVip()) {
                _PF.create_bannerad();
            }
        } else if (type == PlatformType.uc) {
            if (_PF.is_uc()) {
                _PF.create_bannerad();
            }
        } else if (type == PlatformType.oppo) {
            if (_PF.is_oppo() && _G.on_line) {
                // _PF.oppo_isShow_bannerad(true, true);
                _PF.create_bannerad(isTop);
            }
        } else if (type == PlatformType.vivo) {
            if (_PF.is_vivo()) {
                _PF.create_bannerad(isTop);
            }
        }
    }

    destroyBannerad(type: string): void {
        if (type == PlatformType.wx) {
            if (_PF.is_wx() && !_PF.is_tt())
                _PF.destroy_bannerad();
        } else if (type == PlatformType.tt) {
            if (_PF.is_tt())
                _PF.destroy_bannerad();
        } else if (type == PlatformType.vivo) {
            if (_PF.is_vivo()) {
                console.warn('destroy vivo banner')
                _PF.destroy_bannerad();
            }
        } else if (type == PlatformType.oppo) {
            if (_PF.is_oppo() && _PF.oppo_bannerAd) {
                console.log('rm_banner oppo');
                _PF.oppo_bannerAd.hide()
            }
        } else if (type == PlatformType.uc) {
            if (_PF.is_uc())
                _PF.destroy_bannerad();
        } else if (type == PlatformType.ks) {
            if (_PF.is_ks()) {

            }
        } else if (type == PlatformType.yywl) {
            if (_PF.is_yywl()) {
                _PF.destroy_bannerad();
            }
        }
    }
}

export const PlatformType = {
    wx: 'wx',
    tt: 'tt',
    vivo: 'vivo',
    oppo: 'oppo',
    uc: 'uc',
    xiaomi: 'xiaomi',
    ks: 'ks',
    yywl: 'yywl',
}
export const UnlockType = cc.Enum({
    video: 0,
    share: 1,
    coin: 2,
})

let gameHelper = GameHelper.getInstance();
window['gameHelper'] = gameHelper;
gameHelper.isFirstPlay();
export { gameHelper, DrawOption };

setTimeout(() => {

    if (!CC_EDITOR) {
        cc.director.on(cc.Director.EVENT_AFTER_SCENE_LAUNCH, () => {
            window.scene = gameHelper.getCore();
        })
    }
    if (!CC_EDITOR) {
        Object.defineProperties(cc.Node.prototype, {
            'worldPosition': {
                get() {
                    return this.convertToWorldSpaceAR(cc.v2(0, 0))
                },
                set(local) {
                    if (this.parent && this.parent.isValid && local) {
                        this.position = this.parent.convertToNodeSpaceAR(local)
                    }
                },
            },
            'worldX': {
                get() {
                    return this.worldPosition.x;
                },
                set(val) {
                    let wpos = this.worldPosition;
                    this.worldPosition = cc.v2(val, wpos.y);
                },
            },
            'worldY': {
                get() {
                    return this.worldPosition.y;
                },
                set(val) {
                    let wpos = this.worldPosition;
                    this.worldPosition = cc.v2(wpos.x, val);
                },
            },
        })
    }
    cc.Node.prototype.setSwallowTouches = function (is) {
        if (this && this['_touchListener'] && typeof this['_touchListener'].setSwallowTouches == 'function') {
            this['_touchListener'].setSwallowTouches(is);
        }
    }
    cc.Node.prototype.getChildByPath = function (path: string) {
        let child: cc.Node = this;
        if (path) {
            let pathSplitArr = path.split('/').filter((v) => {
                return v != '';
            });
            ;
            while (pathSplitArr.length && child && child.getChildByName(pathSplitArr[0])) {
                let name = pathSplitArr.shift();
                child = child.getChildByName(name);
                // console.log('name', child, pathSplitArr);
            }
            if (pathSplitArr.length > 0) {
                return null;
            }
            return child;
        }
        return null;
    }
    cc.Vec2.distance = function (p1: cc.Vec2, p2: cc.Vec2) {
        return p1.sub(p2).mag();
    }

    cc.Tween.prototype.tweenTag = 0;
    cc.Tween.prototype.tag = function (tag: number): cc.Tween {
        if (!tag) return this;
        this.tweenTag = tag;
        return this;
    }
    cc.Tween.stopAllByTarget = function <T extends Object>(target: T) {
        target['tweenCache'] = target['tweenCache'] || []
        let tweenCache: cc.Tween[] = target['tweenCache'];
        tweenCache.forEach((tw: cc.Tween) => {
            tw.stop();
        });
        tweenCache = [];
        return this;
    }
    cc.Tween.stopByTag = function <T extends Object>(tag: number, target: T) {
        if (typeof tag != "number") {
            target['tweenCache'] = target['tweenCache'] || []
            let tweenCache: cc.Tween[] = target['tweenCache'];
            tweenCache = tweenCache.filter((tw: cc.Tween) => {
                if (tw.tweenTag == tag) {
                    tw.stop();
                    return false
                }
                return true
            });
        }
        return this;
    }
})